package networkTopo;

import atag.runtime.ln.nodeAttributesForGUI.StringAttribute;
import atag.runtime.ln.nodeAttributesForGUI.IntegerAttribute;
import atag.runtime.ln.nodeAttributesForGUI.NodeAttribute;
import atag.runtime.ln.nodeAttributesForGUI.SetAttribute;

import java.awt.*;
import javax.swing.*;
import java.io.*;
import java.lang.Math;

import atag.runtimeForGUI.NodeInfo;
import atag.runtimeForGUI.NodeLocation;

import java.util.*;

public class NetworkTopo {

    JFrame m_parent;

    public static final int X = 0;
    public static final int Y = 1;
    public static final int MAXNODES = 400;
    public int nnodes;
    /*int[][] coords = new int[MAXNODES][2];
public int nnodes, xrange, yrange, radiorange, sensingrange;*/
    public int[][] nbhood = new int[MAXNODES][MAXNODES];
    private int m_xRange;
    private int m_yRange;
    private int m_radioRange;
    private int m_sensingRange;
    public ArrayList<NodeInfo> nodes;
    //public ArrayList<NodeInfo> newNodes;
    private ArrayList<NodeInfo>[] neighborLists;
    public ArrayList<AttributeField> HardwareList;
    public ArrayList<AttributeField> LogicAttrList;

    String topoFilePath = null;

    //String topoFileName = null;

    public NetworkTopo(JFrame parentWindow, String tfP) {
        m_parent = parentWindow;
        topoFilePath = tfP;
    }
/*
  public boolean isNeighbor(int x, int y) {
    if (nbhood[x][y] == 1)
      return true;
    else
      return false;
  }

  public int[] coords(int x) {
    return coords[x];
  }

  public int xcoord(int id) {
    return coords[id][X];
  }

  public int ycoord(int id) {
    return coords[id][Y];
  }
*/


    public int getNumberOfNodes() {
        return this.nodes.size();
    }

    public int getXRange() {
        return this.m_xRange;
    }

    public int getYRange() {
        return this.m_yRange;
    }

    public int getRadioRange() {
        return this.m_radioRange;
    }

    public int getSensingRange() {
        return this.m_sensingRange;
    }

    public String getFilePath() {//Changed here
        return this.topoFilePath;
    }

    public ArrayList<NodeInfo> getNodes() {
        return this.nodes;
    }

    public NodeInfo getNodeInfoByID(int ID) {
        return this.nodes.get(ID);
    }

    public void discoverNeighbors() {
        this.neighborLists = new ArrayList[this.nodes.size()];
        for (int ncount = 0; ncount < this.nodes.size(); ncount++)
            this.neighborLists[ncount] = new ArrayList<NodeInfo>();
        for (int i = 0; i < this.nodes.size() - 1; i++) {
            for (int j = i + 1; j < this.nodes.size(); j++) {
                // upper triangular matrix is traversed
                if (this.nodes.get(i).getMyLocation().distanceFrom(
                        this.nodes.get(j).getMyLocation()) <= this.m_radioRange) {
                    // add these nodes to each other's neighbor lists
                    this.neighborLists[i].add(this.nodes.get(j));
                    this.neighborLists[j].add(this.nodes.get(i));
                }
            }
        }
    }

    public ArrayList<NodeInfo> getNeighborsOf(int nodeID) {
        return this.neighborLists[nodeID];
    }

    /*	public int numberOfNodesWithAttribute(String setName, String memberName) {
            int count = 0;

            SetMembershipPredicate checkPred = new SetMembershipPredicate(
                    memberName, SetMembershipPredicate.IS_IN, setName);

            for (NodeInfo n : this.nodes) {
                // for each node, see if it has that attribute
                Vector attributes = (Vector) n.getNodeAttributes();
                Iterator it = attributes.iterator();
                while (it.hasNext()) {
                    // for each attribute, see if it matches.
                    if (checkPred.matches((NodeAttribute) it.next()))
                        count++;

                }
            }
            return count;
        }
    */
    /**
     * @param givenRegionName The name of the region
     * @returns The number of regions the topology is divided into
     */
    public int numberOfRegions(String givenRegionName) {
        int regionCount = 0;

        Vector<Integer> regionsList = new Vector<Integer>();

        for (NodeInfo n : this.nodes) {
            // for each node, see if it has that attribute
            Vector attributes = (Vector) n.getNodeAttributes();
            Iterator it = attributes.iterator();
            while (it.hasNext()) {
                NodeAttribute atr = (NodeAttribute) it.next();
                if (atr instanceof IntegerAttribute) {
                    // look at the name
                    String regionName = atr.getKey();
                    if (regionName.equalsIgnoreCase(givenRegionName)) {

                        // get regionID
                        int rID = ((Integer) atr.getValue()).intValue();

                        // iterate over the regionList

                        Iterator rIT = regionsList.iterator();
                        boolean found = false;
                        while (rIT.hasNext()) {
                            if (((Integer) rIT.next()).intValue() == rID) {
                                found = true;
                                break;
                            }
                        }
                        // the loop has ended.
                        if (!found) {
                            // add this element. Add to count also
                            regionsList.add(new Integer(rID));
                            regionCount++;
                        }// end if
                    }// end if

                }// end if
            }// end while
        }// end for

        return regionCount;
    }

    public ArrayList<NodeInfo> getNodesWithAttribute(NodeAttribute na) {

        ArrayList<NodeInfo> retArr = new ArrayList<NodeInfo>();

        for (NodeInfo n : this.nodes) {
            // for each node, see if it has that attribute
            Vector attributes = (Vector) n.getNodeAttributes();
            Iterator it = attributes.iterator();
            while (it.hasNext()) {
                // for each attribute, see if it matches.
                if (((NodeAttribute) it.next()).equals(na)) {
                    retArr.add(n);
                    break; // jump out of the while. Continue with the next
                    // node
                }
            }
        }

        return retArr;
    }

    public ArrayList<NodeInfo> getNodesInRegion(String givenRegionName,
                                                int regionID) {

        ArrayList<NodeInfo> retArr = new ArrayList<NodeInfo>();

        for (NodeInfo n : this.nodes) {
            // for each node, see if it has that attribute
            Vector attributes = (Vector) n.getNodeAttributes();
            Iterator it = attributes.iterator();
            while (it.hasNext()) {
                NodeAttribute atr = (NodeAttribute) it.next();
                if (atr instanceof IntegerAttribute) {
                    // look at the name
                    String regionName = atr.getKey();
                    if (regionName.equalsIgnoreCase(givenRegionName)) {

                        // get regionID
                        int rID = ((Integer) atr.getValue()).intValue();

                        if (rID == regionID) {
                            retArr.add(n);
                            break;// get out of the while, continue with next
                            // node
                        }

                    }// end if

                }// end if
            }// end while
        }// end for

        return retArr;
    }

    public String toString() {

        String ret = "Topology: ";
        Iterator<NodeInfo> it = nodes.iterator();
        while (it.hasNext()) {
            NodeInfo nodeInfo = it.next();
            ret = ret.concat("\n" + nodeInfo.toString());
        }
        return ret;
    }

    public void readTopoFromFile() {

        File selectedFile = new File(topoFilePath);
        //System.out.println(selectedFile.getPath() + selectedFile.getName());
        String[] topoArgs;
        StringBuffer contents = new StringBuffer();
        String sContents;

        BufferedReader input = null;
        try {
            input = new BufferedReader(new FileReader(selectedFile));
            String line = null;
            while ((line = input.readLine()) != null) {
                contents.append(line);
                contents.append(" ");
            }
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (input != null) {
                    input.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        sContents = contents.toString();
        topoArgs = sContents.split(" ");

        int i = 0;

        int m_numberOfNodes = Integer.parseInt(topoArgs[i++]);
        nnodes = m_numberOfNodes;//

        // extra functionality -- remove later
        this.HardwareList = new ArrayList<AttributeField>();
        this.LogicAttrList = new ArrayList<AttributeField>();

        this.nodes = new ArrayList<NodeInfo>(m_numberOfNodes);

        // //-----------////

        this.m_xRange = Integer.parseInt(topoArgs[i++]);
        this.m_yRange = Integer.parseInt(topoArgs[i++]);
        this.m_radioRange = Integer.parseInt(topoArgs[i++]);
        this.m_sensingRange = Integer.parseInt(topoArgs[i++]);

        //System.out.println(nnodes + " " + this.m_xRange + " " + this.m_sensingRange);

        while (i < topoArgs.length) {
            int nid = Integer.parseInt(topoArgs[i++]);
            String nPhysicalAddress = topoArgs[i++];
            int nx = Integer.parseInt(topoArgs[i++]);
            int ny = Integer.parseInt(topoArgs[i++]);

            Vector attributes = new Vector();
            // get the region Label
            String[] regionInfo = topoArgs[i++].split(":");
            if (!(regionInfo[0].equalsIgnoreCase("none"))) {
                // add only if there _is_ a region label
                attributes.add(new IntegerAttribute(regionInfo[0], Integer
                        .parseInt(regionInfo[1])));

                //AttributeField tempField = new AttributeField(regionInfo[0], regionInfo[1]);
                int tempIndex = this.isInAttrFieldList(regionInfo[0], LogicAttrList);
                if (tempIndex == -1) {
                    AttributeField tempField = new AttributeField(regionInfo[0], regionInfo[1]);
                    LogicAttrList.add(tempField);
                } else {
                    if (!(LogicAttrList.get(tempIndex).isAttributeValueExist(regionInfo[1]))) {
                        LogicAttrList.get(tempIndex).addAttributeValue(regionInfo[1]);
                    }
                }
            }

            // get the ability Label
            String[] abilityInfo = topoArgs[i++].split(":");
            if (!(abilityInfo[0].equalsIgnoreCase("none"))) {
                int count = 0;
                while (count < abilityInfo.length) {
                    // TODO this now supports only one member per set. Needs to
                    // be modified
                    // done .. using split with commas
                    String setName = abilityInfo[count++];
                    String memberName = abilityInfo[count++];
                    attributes.add(new SetAttribute(setName, memberName
                            .split(",")));
                    //System.out.println("test []!!" +((String) ((NodeAttribute) attributes.lastElement()).getValue())+ " " + memberName);
                    int tempIndex = this.isInAttrFieldList(abilityInfo[0], HardwareList);
                    if (tempIndex == -1) {
                        AttributeField tempField = new AttributeField(abilityInfo[0], abilityInfo[1]);
                        HardwareList.add(tempField);
                        //System.out.println("not in hardware list");
                    } else {
                        if (!(HardwareList.get(tempIndex).isAttributeValueExist(abilityInfo[1]))) {
                            HardwareList.get(tempIndex).addAttributeValue(abilityInfo[1]);
                            //System.out.println("is in hardware list but no such value");
                        }
                    }
                }
            }

            // get the type string
            String type = topoArgs[i++];
            System.out.println("[NetworkTopology] type ="+ type);
            if (type.startsWith("freespot")) {
                attributes.add(new StringAttribute("type", "freespot"));
            } else if (type.startsWith("hostspot")) {
                attributes.add(new StringAttribute("type", "hostspot"));
            } else if (type.startsWith("j2se")) {
                attributes.add(new StringAttribute("type", "j2se"));
            } else if (type.startsWith("android")) {
                attributes.add(new StringAttribute("type", "android"));
            }

            this.nodes.add(new NodeInfo(nid, nPhysicalAddress, new NodeLocation(nx, ny),
                    attributes));
//            if (!(abilityInfo[0].equalsIgnoreCase("none"))) {
//                this.nodes.get(nodes.size()-1).myAbilityName = abilityInfo[0];
//                this.nodes.get(nodes.size()-1).myAbilityValue = abilityInfo[1];
//                this.nodes.get(nodes.size()-1).myRegionName = regionInfo[0];
//                this.nodes.get(nodes.size()-1).myRID = Integer.parseInt(regionInfo[1]);
//                
//            }

        }
        calculateAdjacency();

        this.discoverNeighbors();
    }

    public void writeTopoToFile(String filePath) {

        File outputFile = new File(filePath);
        //System.out.println(selectedFile.getPath() + selectedFile.getName());
        String[] topoArgs;
        StringBuffer contents = new StringBuffer();
        String sContents;

        BufferedWriter output = null;
        try {
            output = new BufferedWriter(new FileWriter(outputFile));
            String line = null;
            line = nnodes + " " + this.m_xRange + " " + this.m_yRange + " " + this.m_radioRange + " " + this.m_sensingRange;
            output.write(line);
            output.newLine();
            for (int i = 0; i < nnodes; i++) {
                int nid = this.nodes.get(i).getMyId();
                String nPhysicalAddress = this.nodes.get(i).getPhysicalAddress();
                int nx = (int) this.nodes.get(i).getMyLocation().getX();
                int ny = (int) this.nodes.get(i).getMyLocation().getY();
                //ArrayList<String> Attribute
                //for (int j = 0; j < this.nodes.get(i).getNodeAttributes().size(); j++) {

                //}

                //Iterator iter = this.nodes.get(i).getNodeAttributes().iterator();
                //while()
                String nRegionName = ((IntegerAttribute) (this.nodes.get(i).getNodeAttributesWithVector().get(0))).getKey();
                String nRID = ((IntegerAttribute) (this.nodes.get(i).getNodeAttributesWithVector().get(0))).getValue().toString();
                String nAbilityName = ((SetAttribute) (this.nodes.get(i).getNodeAttributesWithVector().get(1))).getKey();
                String str_temp = ((SetAttribute) (this.nodes.get(i).getNodeAttributesWithVector().get(1))).getValue().toString();
                String nAbilityValue = str_temp.substring(1, str_temp.length() - 1);

                line = nid + " " + nPhysicalAddress + " " + nx + " " + ny + " " + nRegionName + ":" + nRID + " " + nAbilityName + ":" + nAbilityValue;
                output.write(line);
                output.newLine();
            }

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                if (output != null) {
                    output.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

    }

    public void calculateAdjacency() {
        for (int i = 0; i < this.nnodes; i++)
            for (int j = 0; j < this.nnodes; j++) {
                if ((i != j) && withinRange(i, j))
                    nbhood[i][j] = 1;
                else
                    nbhood[i][j] = 0;
            }
    }

    // Is the point[] within sensing range of node ID <nodeid>
    public boolean isCovered(int nodeid, int[] point) {
        double xdist = Math.pow((double) this.nodes.get(nodeid).getMyLocation().getX() - point[X], 2.0);
        double ydist = Math.pow((double) this.nodes.get(nodeid).getMyLocation().getY() - point[Y], 2.0);
        double dist = Math.sqrt(xdist + ydist);
        if (dist <= m_sensingRange)
            return true;
        else
            return false;

    }

    private boolean withinRange(int first, int second) {
        double xdist = Math.pow((double) this.nodes.get(first).getMyLocation().getX() - this.nodes.get(second).getMyLocation().getX(), 2.0);
        double ydist = Math.pow((double) this.nodes.get(first).getMyLocation().getY() - this.nodes.get(second).getMyLocation().getY(), 2.0);
        double dist = Math.sqrt(xdist + ydist);
        if (dist <= m_radioRange)
            return true;
        else
            return false;
    }

    public int isInAttrFieldList(String name, ArrayList<AttributeField> attrList) {
        int ret = -1;
        for (int i = 0; i < attrList.size(); i++) {
            if (attrList.get(i).isAttributeNameMatch(name)) {
                ret = i;
                //System.out.println(" found the same name!!!!!!!!!!!!!");
                break;
            }
        }
        //System.out.println("found the same name" + ret);
        return ret;
    }

    public NodeIDGroup getNodeIDGroupWithAttribute(NodeAttribute attr) {

        NodeIDGroup retNodeIDGroup = new NodeIDGroup();

        for (int i = 0; i < this.nnodes; i++) {
            if (this.getNodes().get(i).doesHaveAttribute(attr)) {
                retNodeIDGroup.addNodeID(i);
            }
        }
        return retNodeIDGroup;


    }

    public boolean isNodeIdExist(int id) {
        boolean ret = false;
        for (int i = 0; i < this.nodes.size(); i++) {
            if (this.nodes.get(i).getMyId() == id) {
                ret = true;
                break;
            }
        }
        return ret;
    }

    public void addNewNodeToNetwork(int newId, String newPhysicalAddress, NodeLocation newNodeLocation, Collection newAttribute) {
        this.nodes.add(new NodeInfo(newId, newPhysicalAddress, newNodeLocation, newAttribute));
        this.nnodes = this.nodes.size();
//            if (!(abilityInfo[0].equalsIgnoreCase("none"))) {
//                this.nodes.get(nodes.size()-1).myAbilityName = abilityInfo[0];
//                this.nodes.get(nodes.size()-1).myAbilityValue = abilityInfo[1];
//                this.nodes.get(nodes.size()-1).myRegionName = regionInfo[0];
//                this.nodes.get(nodes.size()-1).myRID = Integer.parseInt(regionInfo[1]);
//                
//            }


        calculateAdjacency();

        this.discoverNeighbors();
  }
}